#ifndef  SHINY_CONTAINER_HPP
# define SHINY_CONTAINER_HPP
# include <iostream>
# include "spriteanimate.hpp"
# include <SFML/Graphics.hpp>
# include <SFML/System.hpp>
# include <list>
# include "eventmanager.hpp"
# include "observatory.hpp"

namespace Shiny
{
  namespace Widgets
  {
    class Ui;    
    
    enum Type
    {
      WidgetUnknown,
      WidgetContainer,
      WidgetUi,
      WidgetTextWidget,
      WidgetProgressBar,
      WidgetWidgetList
    };
    
    template<typename T>
    struct type2code { enum { code = WidgetUnknown }; };    
    
    enum AlignType
    {
      Left,
      Right,
      Center,
      None
    };

    enum VAlignType
    {
      Top,
      Bot,
      VCenter,
      VNone
    };

    class  Container : public sf::RenderTexture, public EventManager::Mouse::Listener
    {
      friend class Ui;
    public:
      typedef void (Container::*TransitionMethod)(sf::Sprite&, bool);
      typedef std::list<Container*> Children;

      Container(void);
      virtual ~Container(void);
      bool         operator==(const std::string& right)     { return (_name == right);  }

      template<typename T>
      T*           GetWidget(const std::string& name)
      {
	Container* widget = this->operator[](name);

	if (widget)
	{
	  if (widget->_typeCode == Widgets::type2code<T>::code)
	    return (reinterpret_cast<T*>(widget));
	}
	return (0);
      }

      Container*   operator[](const std::string& name);
      Container&   operator()(const std::string& name);
      Children::iterator     ChildrenBegin(void)            { return (_children.begin()); }
      Children::iterator     ChildrenEnd(void)              { return (_children.end());   }
      virtual void Render(sf::RenderTarget&);
      virtual void PostRender(void)                         {                             }
      void         SetName(const std::string& name)         { _name     = name;           }
      void         SetPosition(const sf::Vector2f& pos)     { _position = pos;            }
      SpriteAnimate& BackgroundSprite(void)                 { return (_sprite);           }
      void         SetBackgroundColor(sf::Color color)      { _backgroundColor = color;   }
      void         SetAlign(AlignType, VAlignType);
      const std::string& GetName(void) const                { return (_name);             }
      void         GetAlign(AlignType& h, VAlignType& v) const              { h = _halign; v = _valign; }
      void         GetPadding(float& l, float& t, float& r, float& b) const { l = _paddingLeftTop.x; t = _paddingLeftTop.y; r = _paddingRightBot.x; b = _paddingRightBot.y; }
      int          GetType(void) const                      { return (_typeCode);         }
      sf::Vector2f GetPosition(void) const                  { return (_position);         }
      Container*   Father(void) const                       { return (_father);           }
      virtual void SetSize(const sf::Vector2f&);
      virtual void ForceVisibility(bool v)                  { _isVisible = v;             }
      virtual void SetVisible(bool v);
      void         SetMovable(bool v)                       { _isMovable = v;             }
      void         SetPadding(float left, float top, float right, float bot);
      void         SetMargin(float left, float top, float right, float bot);
      void         SetAnimationType(TransitionMethod m)     { _transition     = m;        }
      void         SetAnimationSpeed(float speed)           { _animationSpeed = speed;    }
      bool         IsMovable(void) const                    { return (_isMovable);        }
      bool         IsVisible(void) const                    { return (_isVisible);        }
      void         Move(const sf::Vector2f& pos);

      virtual void AddWidget(Container*);
      virtual void DelWidget(Container*);
      virtual void TakeFocus(Container*);
      virtual void SelfFocus(void);

      void          SetMouseAwareness(char flag)            { _mouseAwareness = flag;     }
      unsigned char GetMouseAwareness(void) const           { return (_mouseAwareness);   }
      
      Observatory::Signal<Container*, sf::Event> Clicked;
      Observatory::Signal<Container*, sf::Event> MousePressed;
      Observatory::Signal<Container*, sf::Event> MouseReleased;
      Observatory::Signal<Container*, sf::Event> Hovered;
      Observatory::Signal<Container*, sf::Event> Unhovered;
      Observatory::Signal<Container*, sf::Event> MouseWheel;
      Observatory::Signal<Container*>            MoveFinished;
      Observatory::Signal<Container*>            AnimationFinished;

      virtual void OtherEvents(const sf::Event);
      
      virtual bool mouseEvent(EventManager::Mouse::Event);

      virtual bool EventClick(const sf::Event);
      virtual bool EventMousePressed(const sf::Event);
      virtual bool EventMouseRelease(const sf::Event);
      virtual bool EventHovered(const sf::Event);
      virtual bool EventUnhovered(const sf::Event);
      virtual bool EventWheel(const sf::Event);

      virtual void ExpectedMouseMove(void);
      virtual void ExpectedMouseRelease(void);
      
      virtual void AnimationFade(sf::Sprite&, bool);
      virtual void AnimationZoom(sf::Sprite&, bool);

    protected:
      virtual void RenderSelf(void);
      virtual void SelectAnimation(void);
      void         Register2EventManager(bool);
      void         UpdateAlign(void);
      void         UpdatePosition(void);

      int                _typeCode;
      std::string        _name;
      Children           _children;
      Container*         _father;
      sf::Clock          _refreshClock;
      sf::Vector2f       _position;
      SpriteAnimate      _sprite;
      sf::Sprite         _renderSprite;
      sf::Color          _backgroundColor;
      float              _animationSpeed;
      bool               _hovered;
      bool               _clicked;
      bool               _hasHoverGraphics, _hasActiveGraphics;
      bool               _isMovable;
      bool               _isMoving;
      bool               _isVisible;
      bool               _animationMoving;
      sf::Vector2f       _animationMovingPos;
      sf::Vector2f       _animationMovingStep;

      bool               _transitionIn, _transitionOut;
      TransitionMethod   _transition;
      sf::Color          _fade;
      sf::Vector2f       _scale;

      sf::Vector2f       _mouseLastPos;
      AlignType          _halign;
      VAlignType         _valign;
      sf::Vector2f       _paddingLeftTop;
      sf::Vector2f       _paddingRightBot;
      sf::IntRect        _drawableZone;
      
      enum MouseAwarness
      {
	MouseNone    = 0,
	MouseHover   = 1,
	MouseClick   = 2,
	MouseButton  = 4,
	MouseWheel2  = 8
      };
      unsigned char      _mouseAwareness;
    };

    class TextWidget : public Container
    {
    public:
      TextWidget();
      void               SetTextPosition(sf::Vector2f);
      void               SetText(const std::string& str);
      void               SetFont(const sf::Font&);
      std::string        Text(void) const                { return (_text.GetString()); }
    protected:
      virtual void       RenderSelf(void);
      void               ResizeIfNeeded(void);

      sf::Text           _text;
    };

    class ProgressBar : public Container
    {
    public:
      ProgressBar();

      void              SetProgressBarTexture(const sf::Texture*);
      void              SetPercentage(float v);
      void              SetSide(bool vertical, bool side);
      void              SetColor(const sf::Color);
      float             Percentage(void) const { return (_percentage); }

    protected:
      virtual void      RenderSelf(void);
      void              UpdateProgressTexture(void);
      void              UpdateProgressShape(void);

      float              _displayedPercentage;
      float              _percentage;
      sf::Sprite         _progressBar;
      bool               _vertical, _side;
      sf::RectangleShape _shape;
      sf::Vector2f       _positionAdjust;
    };

    class WidgetList : public Container
    {
    public:
      WidgetList();
      virtual void       AddWidget(Container*);
      virtual void       DelWidget(Container*);
      virtual void       TakeFocus(Container*);
      virtual bool       EventWheel(const sf::Event event);
      void               SetVerticalList(bool v)  { _verticalList = v;    }
      void               SetLeftToRight(bool l2r) { _leftToRight  = l2r ; }
      void               SetSlideEnabled(bool v)  { _slideEnabled = v;    }
      void               SetWheelStep(short step) { _wheelStep    = step; }
      virtual void       PostRender(void);
      
      Observatory::Signal<Container*> SelectionChanged;

    private:
      virtual void       RenderSelf(void);
      void               RefreshOffset(void);
      float              GetListHeight(void);
      float              GetListWidth(void);
      void               UpdateListPosition(void);

      short              _wheelStep;
      bool               _verticalList, _leftToRight, _slideEnabled;
      sf::Vector2f       _offsetSlide;
      sf::Vector2f       _displayedOffset;
      sf::Vector2f       _listSize;
      
      sf::Sprite         _focusSprite;
      sf::RectangleShape _focusRect;
      Container*         _focusWidget;
    };

    template<> struct type2code<Container>   { enum { code = WidgetContainer   }; };
    template<> struct type2code<TextWidget>  { enum { code = WidgetTextWidget  }; };
    template<> struct type2code<ProgressBar> { enum { code = WidgetProgressBar }; };
    template<> struct type2code<WidgetList>  { enum { code = WidgetWidgetList  }; };
    template<> struct type2code<Ui>          { enum { code = WidgetUi          }; };
  }
}

#endif
